package com.fkdx.common.utils;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.BeanWrapper;
import org.springframework.beans.BeanWrapperImpl;

import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.function.Supplier;


/**
 * @author Jiang.Li
 * @version 1.0
 * @description: TODO
 * @date 2022/9/12 21:09
 */
@Slf4j
public class BeanUtil extends BeanUtils {

    public static Object merge(Object sourceBean, Object targetBean) {
        Class sourceBeanClass = sourceBean.getClass();
        Class targetBeanClass = targetBean.getClass();

        Field[] sourceFields = sourceBeanClass.getDeclaredFields();
        for (int i = 0; i < sourceFields.length; i++) {
            Field sourceField = sourceFields[i];
            sourceField.setAccessible(true);
            try {
                Object val = sourceField.get(sourceBean);
                if (val != null) {
                    Field tf = targetBeanClass.getDeclaredField(sourceField.getName());
                    tf.setAccessible(true);
                    if (tf.getGenericType().getTypeName().endsWith("java.lang.Integer")) {
                        tf.set(targetBean, new BigDecimal(val.toString()));
                    } else if(tf.getGenericType().getTypeName().endsWith("java.math.BigDecimal")) {
                        tf.set(targetBean, new BigDecimal(val.toString()));
                    } else if(tf.getGenericType().getTypeName().endsWith("java.lang.Double")) {
                        tf.set(targetBean, Double.parseDouble(val.toString()));
                    } else if(tf.getGenericType().getTypeName().endsWith("java.lang.Boolean")) {
                        tf.set(targetBean, Boolean.parseBoolean(val.toString()));
                    }
                    tf.set(targetBean, val);
                }
            } catch (Exception e) {
                log.error("[ BeanUtil merge error : {}]", e);
            }
        }
        return targetBean;
    }

    public static <S, T> T copyProperties(S sources, Supplier<T> target) {
        T t = target.get();
        copyProperties(sources, t);
        return t;
    }

    public static <S, T> T copyNotEmptyProperties(S sources, T target) {
        copyProperties(sources, target, getNullPropertyNames(sources));
        return target;
    }

    public static <S, T> List<T> copyListProperties(List<S> sources, Supplier<T> target) {
        return copyListProperties(sources, target, null);
    }

    public static <S, T> List<T> copyListProperties(List<S> sources, Supplier<T> target, BeanCopyUtilCallBack<S, T> callBack) {
        List<T> list = new ArrayList<>(sources.size());
        for (S source : sources) {
            T t = target.get();
            copyProperties(source, t);
            list.add(t);
            if (callBack != null) {
                callBack.callBack(source, t);
            }
        }
        return list;
    }
    private static String[] getNullPropertyNames(Object source) {
        BeanWrapper src = new BeanWrapperImpl(source);
        PropertyDescriptor[] pds = src.getPropertyDescriptors();
        Set<String> emptyNames = new HashSet<>();
        for (PropertyDescriptor pd : pds) {
            Object srcValue = src.getPropertyValue(pd.getName());
            if (srcValue == null) {
                emptyNames.add(pd.getName());
            }
        }
        return emptyNames.toArray(new String[emptyNames.size()]);
    }

}

interface BeanCopyUtilCallBack<S, T> {
    void callBack(S s, T t);
}